package org.campuscontent.ui;

import java.awt.Component;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.util.ResourceBundle;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.campuscontent.config.Config;
import org.campuscontent.config.languages.LanguageListener;
import org.campuscontent.config.languages.LanguageManager;
import org.campuscontent.io.AutomataExporter;
import org.campuscontent.io.Base64;
import org.campuscontent.main.MainFrame;
import org.campuscontent.util.GraphInspector;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class ProblemCtrl extends JPanel implements ActionListener, LanguageListener
{
	public MainFrame mf;
	public JTextArea lobjectTitle;
	public JTextArea lobjectDescribtion;
	String[] userDesc;
	private JScrollPane scrollPane;
	
	public JLabel templateLabel1 ;
	public JLabel templateLabel2 ;
	public JComboBox problemTemplate;

	// public JButton edit;
	public JButton saveChanges; // 
	public JButton preview;
	public JButton exitPreview;
	public JButton saveAs;

	GraphInspector graphInspector;

	public ProblemCtrl( MainFrame mf )
	{
		this.mf = mf;
		this.setLayout(null);

		lobjectTitle = new JTextArea();
		lobjectTitle.setLineWrap(true);
		lobjectTitle.setEditable(true);
		lobjectTitle.setBorder( BorderFactory.createTitledBorder( LanguageManager.str( this, "top.name" ) ));
		lobjectTitle.setBounds(0, 0, 140, 40);

		lobjectDescribtion = new JTextArea();
		lobjectDescribtion.setLineWrap(true);
		lobjectDescribtion.setEditable(true);
		scrollPane = new JScrollPane(lobjectDescribtion);
		scrollPane.setBounds(0, 43, 140, 235);
		scrollPane.setBorder(BorderFactory.createTitledBorder( LanguageManager.str( this, "top.task" ) ));

		userDesc = new String[MAX_TEMPLATES];
		ComboRenderer cbr = new ComboRenderer();
        LanguageManager.getInstance().addListener( cbr );

		Integer[] intArray = new Integer[ MAX_TEMPLATES ];
		for (int i = 0; i < MAX_TEMPLATES; i++)
		{
			intArray[i] = new Integer(i);
			userDesc[i] = cbr.getItemAt(i);
		}
		
		templateLabel1 = new JLabel(LanguageManager.str( this, "top.caption1" ));
		templateLabel2 = new JLabel(LanguageManager.str( this, "top.caption2" ));
		
		templateLabel1.setBounds(2,280,135,20);
		templateLabel2.setBounds(2,300,135,20);
		
		problemTemplate = new WideComboBox(intArray);
		// problemTemplate
		problemTemplate.setRenderer( cbr );
		problemTemplate.setMaximumRowCount(3);
		// problemTemplate.setSize(400, 150);
//		problemTemplate.setBorder(BorderFactory.createTitledBorder(Config.PCTemplateLabel_DE));
		problemTemplate.setBounds(2, 328, 135, 25);
		problemTemplate.setSelectedItem(0);
		problemTemplate.addActionListener(this);

		// edit = new JButton(Config.EDIT);
		// edit.setActionCommand(Config.EDIT);
		// edit.setBounds(0, 400, 135, 30);
		// edit.addActionListener(this);

		saveChanges = new JButton(LanguageManager.str( this, "buttons.save" ));
		saveChanges.setActionCommand(Config.SAVE_CHANGES);
		saveChanges.setBounds(2, 360, 135, 30);
		saveChanges.addActionListener(this);

		preview = new JButton(LanguageManager.str( this, "buttons.preview" ));
		preview.setActionCommand(Config.PREVIEW);
		preview.setBounds(2, 430, 135, 30);
		preview.addActionListener(this);

		saveAs = new JButton(LanguageManager.str( this, "buttons.saveas" ));
		saveAs.setActionCommand(Config.SAVE_AS);
		saveAs.setBounds(2, 395, 135, 30);
		saveAs.addActionListener(this);

		exitPreview = new JButton(LanguageManager.str( this, "buttons.exit" ));
		exitPreview.setActionCommand(Config.EXIT_PREVIEW);
		exitPreview.setBounds(2, 430, 135, 30);
		exitPreview.addActionListener(this);
		exitPreview.setVisible(false); // hidden waiting preview command to
		// invoke

		this.add(lobjectTitle);
		this.add(scrollPane);
		this.add(templateLabel1);
		this.add(templateLabel2);
		this.add(problemTemplate);
		// this.add(edit);
		// this.add(discardChanges);
		this.add(saveChanges);
		this.add(saveAs);
		this.add(preview);
		this.add(exitPreview);
		problemTemplate.setVisible(true);
		// discardChanges.setVisible(false);
		saveChanges.setVisible(true);
		
		LanguageManager.getInstance().addListener( this );
	}

	public void update( ResourceBundle resources )
 	{
	    saveChanges.setText( LanguageManager.str( this, "buttons.save"    ) );
	    saveAs     .setText( LanguageManager.str( this, "buttons.saveas"  ) );
	    exitPreview.setText( LanguageManager.str( this, "buttons.preview" ) );
	    preview    .setText( LanguageManager.str( this, "buttons.exit"    ) );
	    
        lobjectTitle.setBorder( BorderFactory.createTitledBorder( LanguageManager.str( this, "top.name" ) ));
        scrollPane.setBorder(BorderFactory.createTitledBorder( LanguageManager.str( this, "top.task" ) ));

        templateLabel1.setText(LanguageManager.str( this, "top.caption1" ));
        templateLabel2.setText(LanguageManager.str( this, "top.caption2" ));

 	}	
	
	@SuppressWarnings("unchecked")
	public void actionPerformed(ActionEvent e)
	{
		int itemIndex;
		JComboBox cb = null;
		userDesc[mf.templateBase.getId() - 1] = lobjectDescribtion.getText();

		if ((e.getSource() instanceof JComboBox) && e.getModifiers() != 0)
		{
			cb = (JComboBox) e.getSource();
			itemIndex = cb.getSelectedIndex() + 1;
			ComboRenderer cbr = (ComboRenderer) cb.getRenderer();
			String sTemp = cbr.getItemAt(itemIndex - 1);

			try
			{
			    AutomatToolbar automatToolbar = mf.ae.getAutomatToolbar();
			    if ( itemIndex == 17 ) // users panel :: multiple
			    {
			        automatToolbar.setVisibleUsersPanel( true );
			    }
			    else
			    {
			        automatToolbar.setVisibleUsersPanel( false );
			    }
			    
			    
				if (userDesc[itemIndex - 1].equals(sTemp))
					lobjectDescribtion.setText(sTemp);
				else
					lobjectDescribtion.setText(userDesc[itemIndex - 1]);
				
				if ((itemIndex == 14) || (itemIndex == 5) )
				{
					preview.setEnabled(false);
					saveChanges.setEnabled(false);
					saveAs.setEnabled(false);
					return;
				}
				{
					mf.templateID = itemIndex;
					preview.setEnabled(true);
					saveChanges.setEnabled(true);
					saveAs.setEnabled(true);

					String str = "org.campuscontent.ui.Template" + itemIndex;
					Class classTemp = Class.forName(str);
					Constructor[] cons = classTemp.getDeclaredConstructors();
					Constructor con;
					for (int i = 0; i < cons.length; i++)
					{
						con = cons[i];
						if (con.getParameterTypes().length == 1)
						{
							// FIXME: the templateBase component should be resizeable
							
							mf.setCreateTempaletFrom("problemCtrl");
							mf.templateBase = (TemplateBase) con.newInstance(new Object[] { mf });
							mf.rootPanel.remove(2);
							// hide previous next submit solution buttons
							mf.templateBase.setDividerLocation(1000);
							mf.rootPanel.add(mf.templateBase, JSplitPane.BOTTOM);
							mf.templateBase.initView();

						}
					}
				}
			} catch (Exception err)
			{
				err.printStackTrace();
			}

		}

		String cmd = e.getActionCommand();
		if (cmd.equals(Config.PREVIEW))
			setPreview();

		if (cmd.equals(Config.EXIT_PREVIEW))
			exitPreview();

		if (cmd.equals(Config.EDIT))
			edit();
		if (cmd.equals(Config.SAVE_CHANGES))
		{
			saveChanges();
		}
		if (cmd.equals(Config.SAVE_AS))
			saveAs();
	}

	public void edit()
	{
		this.problemTemplate.setVisible(true);
		// this.edit.setVisible(false);
		// this.discardChanges.setVisible(true);
		this.saveChanges.setVisible(true);
		this.lobjectTitle.setEditable(true);
		this.lobjectDescribtion.setEditable(true);

	}

	public void discardChanges()
	{
		this.problemTemplate.setVisible(false);
		// this.edit.setVisible(true);
		// this.discardChanges.setVisible(false);
		this.saveChanges.setVisible(false);

		this.lobjectTitle.setEditable(false);
		this.lobjectDescribtion.setEditable(false);

		mf.updateUIFromLObject();
	}

	public void askOwnerChoice()
	{
		Object[] options = { "Yes, please", "No thank!", "Cancel" };

		int n = JOptionPane.showOptionDialog(getWindowForComponent(mf.applet), Config.SAVE_LOBJECT_CHANGES,
				Config.SAVE_CHAGENS_CONFIRMATION, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
				options, options[0]);
		if (n == JOptionPane.YES_OPTION)
		{
			mf.updateToLObjectOverride();
			saveChanges();
			System.out.println("Changes are saved!");
		} else if (n == JOptionPane.NO_OPTION)
		{
			mf.updateToLObjectNew();
			saveChanges();
			System.out.println("Nothing saved");
		} else
		{
			System.out.println("Nothing saved");
		}
	}

	public void askNewUserChoice()
	{
		Object[] options = { "Yes, please", "No thank!" };

		int n = JOptionPane.showOptionDialog(getWindowForComponent(mf.applet), Config.SAVE_LOBJECT_CHANGES_NEWUSER,
				Config.SAVE_CHAGENS_CONFIRMATION, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
				options, options[0]);
		if (n == JOptionPane.YES_OPTION)
		{
			mf.updateToLObjectNew();
			saveChanges();
			System.out.println("Changes are saved!");
		} else if (n == JOptionPane.NO_OPTION)
		{

			System.out.println("Nothing saved");
		}
	}

	public void saveAs()
	{
		String title = lobjectTitle.getText().trim();
		if (title.length() <= 0)
		{
			JOptionPane.showMessageDialog(getWindowForComponent(mf.applet), Config.PCNameNeeded);
			return;
		}
		String s = (String) JOptionPane.showInputDialog(getWindowForComponent(mf.applet), Config.PCSaveAs, 
				Config.SAVE_AS, JOptionPane.PLAIN_MESSAGE, null, null, title);

		// s == null if user chose cancel then do nothing
		// If a string was returned, say so.
		if (s !=null)
		{
			if ((s.length() > 0))
			{
				lobjectTitle.setText(s);
				mf.loidMain = "-1";
				saveChanges();
				return;
			} else
			{
				JOptionPane.showMessageDialog(getWindowForComponent(mf.applet), Config.PCNameNeeded);
				return;
			}
		}
	}

	public void saveChanges()
	{
		// this.problemTemplate.setVisible(false);
		// // this.edit.setVisible(true);
		// // this.discardChanges.setVisible(false);
		// this.saveChanges.setVisible(false);
		//
		// this.lobjectName.setEditable(false);
		// this.lobjectDescribtion.setEditable(false);
		// int selected = problemTemplate.getSelectedIndex() + 1;

		graphInspector = new GraphInspector(mf.ae.getGraph());

		if (graphInspector.validate() != "valid")
		{
			JOptionPane.showMessageDialog(getWindowForComponent(mf.applet), graphInspector.validate());
			return;
		}

		// Compare the "Input Symbol" part

		if (mf.templateBase.setPreview())
		{
			mf.templateBase.exitPreview();
			if (mf.templateBase.updateToLObjectNew())
			{	
				if (publishToRepositroyDB(getXmlStrFromCurrentLObject()))
			JOptionPane.showMessageDialog(getWindowForComponent(mf.applet),
					Config.PCSaved + mf.lobject.getLoid());
			}
		}

	}

	public Frame getWindowForComponent(Component parentComponent)
	{
		if (parentComponent instanceof Frame)
			return (Frame) parentComponent;
		return getWindowForComponent(parentComponent.getParent());
	}

	public void setPreview()
	{
		// first have a look at title and requirements;
		String title = lobjectTitle.getText().trim();
		String requirments = lobjectDescribtion.getText().trim();

		if (title.length() <= 0)
		{
			JOptionPane.showMessageDialog(getWindowForComponent(mf.applet), Config.PCNameNeeded);
			return;
		}

		if (requirments.length() <= 0)
		{
			JOptionPane.showMessageDialog(getWindowForComponent(mf.applet), Config.PCReqNeeded);
			return;
		}

		// if set preview succeed then do following action else do nothing by
		// return.
		if (!(mf.templateBase.setPreview()))
		{
			// exitPreview();
			return;
		}

		mf.ae.setEnable(false);
		mf.ae.toolbar.setVisible(false);

		this.lobjectTitle.setEditable(false);
		this.lobjectDescribtion.setEditable(false);
		
		this.templateLabel1.setVisible(false);
		this.templateLabel2.setVisible(false);

		this.problemTemplate.setVisible(false);
		this.saveChanges.setVisible(false);
		this.saveAs.setVisible(false);
		this.preview.setVisible(false);
		this.exitPreview.setVisible(true);
	}

	public void exitPreview()
	{
		mf.templateBase.exitPreview();

		mf.ae.toolbar.setVisible(true);
		mf.ae.setEnable(true);
		this.lobjectTitle.setEditable(true);
		this.lobjectDescribtion.setEditable(true);

		this.templateLabel1.setVisible(true);
		this.templateLabel2.setVisible(true);
		
		this.problemTemplate.setVisible(true);
		this.saveChanges.setVisible(true);
		this.saveAs.setVisible(true);
		this.preview.setVisible(true);
		this.exitPreview.setVisible(false);
	}

	public String getXmlStrFromCurrentLObject()
	{
		try
		{
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();

			Element xmlRoot = doc.createElement("lobject");
			doc.appendChild(xmlRoot);

			// lobject

			Element childOfID = doc.createElement("id");
			String temp = mf.lobject.getLoid();
			childOfID.setTextContent(temp);

			Element childOfAid = doc.createElement("aid");
			temp = mf.lobject.getAid();
			childOfAid.setTextContent(temp);

			Element childOfSource = doc.createElement("source");
			temp = mf.automata.getSource() + "";
			childOfSource.setTextContent(temp);

			Element childOfAname = doc.createElement("aname");
			childOfAname.setTextContent(mf.automata.getName());

			Element childOfGraphStr = doc.createElement("gstring");
			String graphStr = mf.automata.getGraphStr();
			byte[] buf = new byte[graphStr.length()];
			buf = graphStr.getBytes();
			buf = Base64.encode(buf);
			String graphStrEncoded = new String(buf);
			childOfGraphStr.setTextContent(graphStrEncoded);

			Element childOfTitle = doc.createElement("title");
			childOfTitle.setTextContent(mf.lobject.getTitle());

			Element childOfOwner = doc.createElement("owner");
			childOfOwner.setTextContent(mf.lobject.getOwner());

			Element childOfDesc = doc.createElement("desc");
			String descStr = mf.lobject.getDesc();
			byte[] bufDesc = new byte[descStr.length()];
			bufDesc = descStr.getBytes();
			bufDesc = Base64.encode(bufDesc);
			String descStrEncoded = new String(bufDesc);
			childOfDesc.setTextContent(descStrEncoded);

			Element childOfSolution = doc.createElement("solution");
			String answerStr = mf.lobject.getSolution();
			byte[] bufAnswer = new byte[answerStr.length()];
			bufAnswer = answerStr.getBytes();
			bufAnswer = Base64.encode(bufAnswer);
			String answerStrEncoded = new String(bufAnswer);
			childOfSolution.setTextContent(answerStrEncoded);

			Element childOfTemplate = doc.createElement("template");
			temp = mf.lobject.getTemplate() + "";
			childOfTemplate.setTextContent(temp);

			Element childOfConfigure = doc.createElement("configure");
			childOfConfigure.setTextContent(mf.lobject.getConfig());

			AutomataExporter exporter = AutomataExporter.getInstance();
			Element childOfImage = doc.createElement("image");
			childOfImage.setTextContent(exporter.getAutomataImageEncoded(mf.ae.graph));

			xmlRoot.appendChild(childOfID);
			xmlRoot.appendChild(childOfAid);
			xmlRoot.appendChild(childOfSource);
			xmlRoot.appendChild(childOfAname);
			xmlRoot.appendChild(childOfGraphStr);
			xmlRoot.appendChild(childOfTitle);
			xmlRoot.appendChild(childOfOwner);
			xmlRoot.appendChild(childOfDesc);
			xmlRoot.appendChild(childOfSolution);
			xmlRoot.appendChild(childOfTemplate);
			xmlRoot.appendChild(childOfConfigure);
			xmlRoot.appendChild(childOfImage);

			// ///////////////
			// Output the XML

			// set up a transformer
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans;
			trans = transfac.newTransformer();

			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");

			// create string from xml tree
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(doc);
			try
			{
				trans.transform(source, result);
			} catch (TransformerException e)
			{
				e.printStackTrace();
				System.out.println("tranfoming from source have a problem");
			}
			String xmlString = sw.toString();

			// print xml

			return xmlString;
		} catch (ParserConfigurationException e)
		{
			e.printStackTrace();
			return "New Parser has a wrong!";
		} catch (TransformerConfigurationException e)
		{
			e.printStackTrace();
			return "New Transformer have a wrong	";
		}

	}

	public void publishToRepositroyFile(String str)
	{
		if (str.equals("") || str == null)
		{
			System.out.println("Cannoot publish nothing!");
		} else
		{
			try
			{
				System.out.println(str);

				mf.phpQuery.add("query_mode", Integer.toString(Config.PHP_QUERY_WRITE_TO_FILE));
				mf.phpQuery.add("lobject", str);

				DataInputStream dis = mf.phpQuery.query();

				System.out.println(dis.toString());

				// get the return loid and aid
				Element currentRoot, tmpElement;
				NodeList currentNodeList, currentTxtList;
				NodeList currentRootList;

				// Construct the document from the inputstream

				DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
				DocumentBuilder docBuilder;

				docBuilder = docBuilderFactory.newDocumentBuilder();

				Document doc = docBuilder.parse(dis);

				// normalize text representation
				doc.getDocumentElement().normalize();

				// get root of document
				currentRootList = doc.getElementsByTagName("lobject");
				currentRoot = (Element) currentRootList.item(0);

				// get the lobject id
				currentNodeList = currentRoot.getElementsByTagName("id");
				tmpElement = (Element) currentNodeList.item(0);
				currentTxtList = tmpElement.getChildNodes();
				mf.lobject.setAid(((Node) currentTxtList.item(0)).getNodeValue().trim());

				// get the automata id
				currentNodeList = currentRoot.getElementsByTagName("aid");
				tmpElement = (Element) currentNodeList.item(0);
				currentTxtList = tmpElement.getChildNodes();
				mf.automata.setId(((Node) currentTxtList.item(0)).getNodeValue().trim());
				System.out.print("lobject id: " + mf.lobject.getAid() + ", automata id: " + mf.automata.getId());
			} catch (ParserConfigurationException e)
			{
				e.printStackTrace();
			} catch (SAXException e)
			{
				e.printStackTrace();
			}

			catch (IOException e)
			{
				e.printStackTrace();

			}
		}

	}

	public boolean publishToRepositroyDB(String str)
	{
		if (str.equals("") || str == null)
		{
			System.out.println("Cannoot publish nothing!");
			return false;
		} else
		{
			try
			{
				System.out.println(str);

				mf.phpQuery.add("query_mode", Integer.toString(Config.PHP_QUERY_WRITE));
				mf.phpQuery.add("lobject", str);

				DataInputStream dis = mf.phpQuery.query();

				System.out.println(dis.toString());

				// get the return loid and aid
				Element currentRoot, tmpElement;
				NodeList currentNodeList, currentTxtList;
				NodeList currentRootList;

				// Construct the document from the inputstream

				DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
				DocumentBuilder docBuilder;

				docBuilder = docBuilderFactory.newDocumentBuilder();

				Document doc = docBuilder.parse(dis);

				// normalize text representation
				doc.getDocumentElement().normalize();

				// get root of document
				currentRootList = doc.getElementsByTagName("lobject");
				currentRoot = (Element) currentRootList.item(0);

				// get the lobject id
				currentNodeList = currentRoot.getElementsByTagName("id");
				tmpElement = (Element) currentNodeList.item(0);
				currentTxtList = tmpElement.getChildNodes();
				String loidTmp = ((Node) currentTxtList.item(0)).getNodeValue().trim();
				mf.lobject.setLoid(loidTmp);

				// get the automata id
				currentNodeList = currentRoot.getElementsByTagName("aid");
				tmpElement = (Element) currentNodeList.item(0);
				currentTxtList = tmpElement.getChildNodes();
				String aidTmp = ((Node) currentTxtList.item(0)).getNodeValue().trim();
				mf.automata.setId(aidTmp);

				// set the automata id of current LObject
				mf.lobject.setAid(aidTmp);
				// set the source of current automata point to the id of current
				// LObject
				mf.automata.setSource(loidTmp);
				mf.loidMain = loidTmp;
				System.out.print("lobject id: " + mf.lobject.getLoid() + ", automata id: " + mf.automata.getId());
			} catch (ParserConfigurationException e)
			{
				e.printStackTrace();
				return false;
			} catch (SAXException e)
			{
				e.printStackTrace();
				return false;
			}

			catch (IOException e)
			{
				e.printStackTrace();
				JOptionPane.showMessageDialog(getWindowForComponent(mf.applet), Config.PCCannotSave);
				return false;

			}
		}
		return true;
		
	}

	public static final int MAX_TEMPLATES = 17;
}
